package eduware.util;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import eduware.util.crypt.EncryptedReader;
import eduware.util.crypt.EncryptedWriter;
import eduware.util.exception.ChaocipherException;

/**
 * Provides static methods to get files from different resources.
 * 
 * @author Florian Bausch
 * 
 */
public class FileHandler {

	/**
	 * Loads an encrypted file from a specific URL with a given character set
	 * and opens it as <code>EncryptedReader</code>.
	 * 
	 * @param urlOrFile
	 *            Exact URL of the file (full path and "http://") or a path to a
	 *            file.
	 * @param charSet
	 *            A character set.
	 * @param timeout
	 *            Time (milliseconds) after that the connection times out.
	 * @return An <code>EncryptedReader</code>, if the file could be opened,
	 *         <code>null</code> else.
	 */
	public static EncryptedReader getEncryptedReader(String urlOrFile,
			String charSet, int timeout) {
		try {
			URL url = new URL(urlOrFile);
			URLConnection connection = url.openConnection();
			connection.setConnectTimeout(timeout);
			connection.setReadTimeout(timeout);
			return new EncryptedReader(new InputStreamReader(
					connection.getInputStream(), charSet));
		} catch (Exception e) {
			try {
				return new EncryptedReader(new InputStreamReader(
						new FileInputStream(new File(urlOrFile)), charSet));
			} catch (FileNotFoundException e1) {
				e1.printStackTrace();
				return null;
			} catch (UnsupportedEncodingException e1) {
				e1.printStackTrace();
				return null;
			} catch (ChaocipherException e1) {
				e1.printStackTrace();
				return null;
			}
		}
	}

	/**
	 * Loads an EncryptedWriter form the specified file path.
	 * 
	 * @param file
	 *            The file path.
	 * @return The EncryptedWriter of the file.
	 */
	public static EncryptedWriter getEncryptedWriter(String file) {
		File f = new File(file);
		f.getParentFile().mkdirs();
		try {
			return new EncryptedWriter(new OutputStreamWriter(
					new FileOutputStream(f), "UTF-8"));
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		} catch (ChaocipherException e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * Enables you to read an encrypted XML document.
	 * 
	 * @param file
	 *            The encrypted XML file.
	 * @param timeout
	 *            Time (milliseconds) after that the connection times out.
	 * @return A Document object or null, if an error occurred.
	 */
	public static Document getEncryptedXMLDocument(String file, int timeout) {
		StringBuffer read = new StringBuffer();
		String current;
		EncryptedReader buff;
		DocumentBuilder docbuilder;

		// Get the file as EncryptedReader
		if ((buff = FileHandler.getEncryptedReader(file, "UTF-8", timeout)) == null)
			return null;

		// Read all lines and store them
		try {
			while ((current = buff.readLine()) != null) {
				read.append(current);
			}
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		} catch (NullPointerException e) {
			e.printStackTrace();
			return null;
		}

		// Create the DocumentBuiler
		try {
			docbuilder = DocumentBuilderFactory.newInstance()
					.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
			return null;
		}
		docbuilder.setErrorHandler(null);

		// Read the file content in UTF-8 format
		try {
			return docbuilder
					.parse(new BufferedInputStream(new ByteArrayInputStream(
							read.toString().getBytes("UTF-8"))));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return null;
		} catch (SAXException e) {
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * Loads a file from an URL or file path with a given character set and
	 * opens it as <code>BufferedReader</code>.
	 * 
	 * @param urlOrFile
	 *            Exact URL of the file (full path and "http://") or path to a
	 *            file on the local file system.
	 * @param charSet
	 *            A character set
	 * @param timeout
	 *            Time (milliseconds) after that the connection times out.
	 * @return A <code>BufferedReader</code>, if the file could be opened,
	 *         <code>null</code> else.
	 */
	public static BufferedReader getReader(String urlOrFile, String charSet,
			int timeout) {
		try {
			URL url = new URL(urlOrFile);
			URLConnection connection = url.openConnection();
			connection.setConnectTimeout(timeout);
			connection.setReadTimeout(timeout);
			return new BufferedReader(new InputStreamReader(
					connection.getInputStream(), charSet));
		} catch (Exception e) {
			try {
				return new BufferedReader(new InputStreamReader(
						new FileInputStream(new File(urlOrFile)), charSet));
			} catch (FileNotFoundException e1) {
				return null;
			} catch (UnsupportedEncodingException e1) {
				return null;
			}
		}
	}

	/**
	 * Loads a BufferedWriter form the specified file path.
	 * 
	 * @param file
	 *            The file path.
	 * @return The BufferedWriter of the file.
	 */
	public static BufferedWriter getWriter(String file) {
		File f = new File(file);
		f.getParentFile().mkdirs();
		try {
			return new BufferedWriter(new FileWriter(f));
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * With this method you get a <code>Document</code> representation of a
	 * file.
	 * 
	 * @param file
	 *            The file that shall be opened
	 * @param timeout
	 *            Time (milliseconds) after that the connection times out.
	 * @return A representation of the file as <code>Document</code>
	 */
	public static Document getXMLDocument(String file, int timeout) {
		StringBuffer read = new StringBuffer();
		String current;
		BufferedReader buff;
		DocumentBuilder docbuilder;

		// Get the file as BufferedReader
		if ((buff = FileHandler.getReader(file, "UTF-8", timeout)) == null)
			return null;

		// Read all lines and store them
		try {
			while ((current = buff.readLine()) != null) {
				read.append(current);
			}
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		} catch (NullPointerException e) {
			e.printStackTrace();
			return null;
		}

		// Create the DocumentBuiler
		try {
			docbuilder = DocumentBuilderFactory.newInstance()
					.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
			return null;
		}
		docbuilder.setErrorHandler(null);

		// Read the file content in UTF-8 format
		try {
			return docbuilder
					.parse(new BufferedInputStream(new ByteArrayInputStream(
							read.toString().getBytes("UTF-8"))));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return null;
		} catch (SAXException e) {
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public static void reloadXMLFile(String filename, String localFilename, Boolean isEncrypted) throws FileNotFoundException {
		BufferedReader buff;
		StringBuilder sb = new StringBuilder();
		sb.append("http://www.fbausch.de/se/");
		sb.append(filename);
		String url = sb.toString();
		
		if(isEncrypted) {
			if ((buff = FileHandler.getEncryptedReader(url,"UTF8", 1000)) == null) throw new FileNotFoundException("Could not load configuration file");
			else {
				String cnt = FileHandler.readWholeFile(buff);
				BufferedWriter fout = FileHandler.getEncryptedWriter(localFilename);
				try {
					fout.write(cnt);
				} catch (IOException e) {
					e.printStackTrace();
					throw new FileNotFoundException("Could not load configuration file");
				}
			}
		}
		else {
			if ((buff = FileHandler.getReader(url,"UTF8", 1000)) == null) throw new FileNotFoundException("Could not load configuration file");
			else {
				String cnt = FileHandler.readWholeFile(buff);
				BufferedWriter fout = FileHandler.getWriter(localFilename);
				try {
					fout.write(cnt);
				} catch (IOException e) {
					e.printStackTrace();
					throw new FileNotFoundException("Could not load configuration file");
				}
			}
		}
	}
	
	public static String readWholeFile (BufferedReader buff) {
		// Read all lines and store them
		String current;
		StringBuffer read = new StringBuffer();
		
		try {
			while ((current = buff.readLine()) != null) {
				read.append(current);
			}
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		} catch (NullPointerException e) {
			e.printStackTrace();
			return null;
		}
		
		return read.toString();
	}
	
}
